package ast

import (
	"bytes"
	"monkey/token"
	"strings"
)

type Node interface{
	TokenLiteral() string//仅用于调试和测试
	//返回与其关联的词法单元的字面量

	String() string//该方法既可以在调试时
	//打印AST节点，也可以用来比较AST节点
}

type Statement interface{//语句接口
	Node
	statementNode()
}

type Expression interface{//表达式接口
	Node 
	expressionNode()
}

type Program struct{
	Statements []Statement
	//这个Program节点将成为语法分析器生成的每个AST的根节点
}

func (p *Program) TokenLiteral() string{
	if len(p.Statements) > 0{
		return p.Statements[0].TokenLiteral()
	}else{
		return ""
	}
}

func (p *Program) String() string{
	var out bytes.Buffer
	for _,s := range p.Statements{
		out.WriteString(s.String())
	}
	return out.String()
}
type LetStatement struct{
	Token token.Token//token.LET词法单位
	Name *Identifier//Name用来保存绑定的标识符
	Value Expression//Value为产生值的表达式
}
//statementNode和TokenLiteral这两个方法分别用来实现Statement接口和Node接口
func (ls *LetStatement) statementNode(){}

func (ls *LetStatement) TokenLiteral() string{
	return ls.Token.Literal
}

func (ls *LetStatement) String() string{
	var out bytes.Buffer
	out.WriteString(ls.TokenLiteral() + " ")
	out.WriteString(ls.Name.String())
	out.WriteString(" = ")
	if ls.Value != nil{
		out.WriteString(ls.Value.String())
	}
	out.WriteString(";")
	return out.String()
}
type Identifier struct{
	Token token.Token//toke.IDENT词法单元
	Value string
}

func (i *Identifier) expressionNode(){}

func (i *Identifier) TokenLiteral() string{
	return i.Token.Literal
}

func (i *Identifier) String() string{
	return i.Value
}
type ReturnStatement struct{
	Token token.Token//'return'词法单位
	ReturnValue Expression
}

func (rs *ReturnStatement) statementNode(){}

func (rs *ReturnStatement) TokenLiteral() string{
	return rs.Token.Literal
}

func (rs *ReturnStatement) String() string{
	var out bytes.Buffer
	out.WriteString(rs.TokenLiteral()+" ")
	if rs.ReturnValue != nil{
		out.WriteString(rs.ReturnValue.String())
	}
	out.WriteString(";")
	return out.String()
}

type ExpressionStatement struct{
	Token token.Token//该表达式中的第一个词法单位
	Expression Expression//保存表达式的Expression字段
}
/*st.ExpressionStatement实现了ast.Statement接口，
这意味着表达式语句可以添加到ast.Program的Statements切片中
，这就是添加ast.ExpressionStatement的原因。*/
func (es *ExpressionStatement) statementNode(){}

func (es *ExpressionStatement) TokenLiteral()string{
	return es.Token.Literal
}

func (es *ExpressionStatement) String() string{
	if es.Expression != nil{
		return es.Expression.String()
	}
	return ""
}

type IntegerLiteral struct{
	Token token.Token
	Value int64
}

func (il *IntegerLiteral) expressionNode(){}
func (il *IntegerLiteral) TokenLiteral() string{
	return il.Token.Literal
}
func (il *IntegerLiteral) String() string{
	return il.Token.Literal
}

type PrefixExpression struct{
	Token token.Token//前缀词法单元,如!
	Operator string//Operator是包含"-"或"!"的字符串
	Right Expression//Right字段包含运算符右边的表达式
}

func (pe *PrefixExpression) expressionNode(){}

func (pe *PrefixExpression) TokenLiteral() string{
	return pe.Token.Literal
}

func (pe *PrefixExpression) String() string{
	var out bytes.Buffer
	out.WriteString("(")
	out.WriteString(pe.Operator)
	out.WriteString(pe.Right.String())
	out.WriteString(")")
	return out.String()
}

type InfixExpression struct{
	Token token.Token//运算符词法单元，如+
	Left Expression
	Operator string
	Right Expression
}

func (ie *InfixExpression) expressionNode(){}
func (ie *InfixExpression) TokenLiteral() string{
	return ie.Token.Literal
}
func (ie *InfixExpression) String() string{
	var out bytes.Buffer

	out.WriteString("(")
	out.WriteString(ie.Left.String())
	out.WriteString(" "+ie.Operator+" ")
	out.WriteString(ie.Right.String())
	out.WriteString(")")

	return out.String()
}

type Boolean struct{
	Token token.Token
	Value bool
}
func (b *Boolean) expressionNode(){}
func (b *Boolean) TokenLiteral() string{
	return b.Token.Literal
}
func (b *Boolean) String() string{
	return b.Token.Literal
}

type IfExpression struct{
	Token token.Token//"if"词法单位
	Condition Expression
	Consequence *BlockStatement
	Alternative *BlockStatement
}

func (ie *IfExpression) expressionNode(){}
func (ie *IfExpression) TokenLiteral() string{
	return ie.Token.Literal
}
func (ie *IfExpression) String() string{
	var out bytes.Buffer
	out.WriteString("if")
	out.WriteString(ie.Condition.String())
	out.WriteString(" ")
	out.WriteString(ie.Condition.String())
	
	if ie.Alternative != nil{
		out.WriteString("else ")
		out.WriteString(ie.Alternative.String())
	}
	return out.String()
}

type BlockStatement struct{
	Token token.Token//'{'词法单位
	Statements []Statement
}

func (bs *BlockStatement) statementNode(){}
func (bs *BlockStatement) TokenLiteral()string{
	return bs.Token.Literal
}
func (bs *BlockStatement) String()string{
	var out bytes.Buffer
	for _,s :=range bs.Statements{
		out.WriteString(s.String())
	}
	return out.String()
}

type FunctionLiteral struct{
	Token token.Token//'fn'词法单位
	Parameters []*Identifier//参数列表
	Body *BlockStatement//块语句
}

func (fl *FunctionLiteral) expressionNode(){}
func (fl *FunctionLiteral) TokenLiteral() string{
	return fl.Token.Literal
}
func (fl *FunctionLiteral) String() string{
	var out bytes.Buffer
	params := []string{}
	for _,p := range fl.Parameters{
		params = append(params, p.String())
	}
	out.WriteString(fl.TokenLiteral())
	out.WriteString("(")
	out.WriteString(strings.Join(params,", "))
	out.WriteString(") ")
	out.WriteString(fl.Body.String())

	return out.String()
}

//调用表达式
type CallExpression struct{
	Token token.Token//"("词法单位
	Function Expression//标识符或者函数字面量
	Arguments []Expression
}

func (ce *CallExpression) expressionNode(){}
func (ce *CallExpression) TokenLiteral() string{
	return ce.Token.Literal
}
func (ce *CallExpression) String() string{
	var out bytes.Buffer
	args := []string{}
	for _,a := range ce.Arguments{
		args = append(args, a.String())
	}
	out.WriteString(ce.Function.String())
	out.WriteString("(")
	out.WriteString(strings.Join(args,", "))
	out.WriteString(")")
	return out.String()
}

//添加string类型的AST节点
type StringLiteral struct{
	Token token.Token
	Value string
}
//字符串字面量是表达式，而不是语句
func (sl *StringLiteral) expressionNode() {}
func (sl *StringLiteral) TokenLiteral() string{
	return sl.Token.Literal
}
func (sl *StringLiteral) String() string{
	return sl.Token.Literal
}
//数组字面量
type ArrayLiteral struct{
	Token token.Token//'['词法单元
	Elements []Expression
}

func (al *ArrayLiteral) expressionNode(){}

func (al *ArrayLiteral) TokenLiteral() string{
	return al.Token.Literal
}

func (al *ArrayLiteral) String() string{
	var out bytes.Buffer
	elements := []string{}
	for _,el := range al.Elements{
		elements = append(elements, el.String())
	}
	out.WriteString("[")
	out.WriteString(strings.Join(elements,", "))
	out.WriteString("]")
	return out.String()
}

//索引运算符表达式
type IndexExpression struct{
	Token token.Token//'['词法单位
	Left Expression//Left是正在访问的对象,可以是任何类型，包括标识符、数组字面量、函数调用等
	Index Expression//Index也是如此，可以是任何表达式
}

func (ie *IndexExpression) expressionNode() {}
func (ie *IndexExpression) TokenLiteral() string{
	return ie.Token.Literal
}
func (ie *IndexExpression) String() string{
	var out bytes.Buffer

	out.WriteString("(")
	out.WriteString(ie.Left.String())
	out.WriteString("[")
	out.WriteString(ie.Index.String())
	out.WriteString("])")

	return out.String()
}

//哈希表字面量
type HashLiteral struct{
	Token token.Token//'{'词法单位
	Pairs map[Expression]Expression
}

func (hl *HashLiteral) expressionNode() {}
func (hl *HashLiteral) TokenLiteral() string{
	return hl.Token.Literal
}
func (hl *HashLiteral) String() string{
	var out bytes.Buffer

	pairs := []string{}
	for key,value := range hl.Pairs{
		pairs = append(pairs, key.String()+":"+value.String())
	}
	out.WriteString("{")
	out.WriteString(strings.Join(pairs,", "))
	out.WriteString("}")
	return out.String()
}
//宏字面量
type MacroLiteral struct{
	Token token.Token
	Parameters []*Identifier
	Body *BlockStatement
}

func (ml *MacroLiteral) expressionNode(){}
func (ml *MacroLiteral) TokenLiteral() string{return ml.Token.Literal}
func (ml *MacroLiteral) String() string{
	var out bytes.Buffer

	params := []string{}
	for _,p := range ml.Parameters{
		params = append(params, p.String())
	}
	out.WriteString(ml.TokenLiteral())
	out.WriteString("(")
	out.WriteString(strings.Join(params,", "))
	out.WriteString(")")
	out.WriteString(ml.Body.String())
	return out.String()
}